package my.mall.admin.api.config

import com.auth0.jwt.exceptions.TokenExpiredException
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import my.mall.admin.api.service.AdminService
import my.mall.common.annotation.Slf4j
import my.mall.common.annotation.Slf4j.Companion.log
import my.mall.common.bean.JsonParser
import my.mall.common.enums.RespMetaEnum
import my.mall.common.util.JwtUtils
import my.mall.common.util.RespUtils
import org.springframework.http.*
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerInterceptor
import java.io.IOException
import java.nio.charset.StandardCharsets

@Slf4j
@Component
class UserAuthInterceptor(
    private val jsonParser: JsonParser,
    private val adminService: AdminService
) : HandlerInterceptor {

    @Throws(IOException::class)
    override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
        if (HttpMethod.OPTIONS.name() == request.method) {
            return true
        }
        if (handler !is HandlerMethod) {
            return true
        }
        val preAuthorize = handler.getMethodAnnotation(PreAuthorize::class.java)
        val needAuth = preAuthorize != null
        val token = request.getHeader(HttpHeaders.AUTHORIZATION)
        var adminId: Long? = null
        if (StringUtils.hasLength(token)) {
            try {
                adminId = JwtUtils.verifyToken(token)
            } catch (e: TokenExpiredException) {
                return sendResponse(response, RespMetaEnum.NOT_LOGIN)
            } catch (e: Throwable) {
                log.warn("parse token failed!", e)
            }
        }
        if (needAuth && adminId == null) {
            return sendResponse(response, RespMetaEnum.NOT_LOGIN)
        }
        request.setAttribute("adminId", adminId)
        val storeIdStr = request.getHeader("Store-ID")
        var storeId: Long? = null
        if (StringUtils.hasLength(storeIdStr)) {
            try {
                storeId = storeIdStr.toLong()
            } catch (e: NumberFormatException) {
                log.warn("parse storeId failed!", e)
            }
        }
        if (needAuth && storeId == null) {
            return sendResponse(response, RespMetaEnum.NO_STORE)
        }
        request.setAttribute("storeId", storeId)
        if (!needAuth) {
            return true
        }
        val resourceList = adminService.getResourceList(storeId!!, adminId!!)
        for (resource in resourceList) {
            for (auth in preAuthorize!!.value) {
                if (auth == resource.resourceKey) {
                    return true
                }
            }
        }
        return false
    }

    @Throws(IOException::class)
    private fun sendResponse(response: HttpServletResponse, respMetaEnum: RespMetaEnum): Boolean {
        response.status = HttpStatus.OK.value()
        response.contentType = MediaType.APPLICATION_JSON_VALUE
        response.characterEncoding = StandardCharsets.UTF_8.name()
        response.writer.write(jsonParser.formatObjToJson(RespUtils.warning<Any>(respMetaEnum)))
        return false
    }
}
